﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Globalization;
using Newtonsoft.Json.Linq;
using System.Threading;

namespace WindowsService
{
    public partial class MyService : ServiceBase
    {
        public MyService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            run();
        }
        protected override void OnStop()
        {
            textWriter($"{DateTime.Now},服务停止！", log);
        }

        private IniFile ini;
        private ProcessInfo info;
        private static string path = @"C:\monitor\json.txt";
        private static string log = @"C:\monitorLog.txt";
        private static string copyJson = @"C:\copyJson.txt";
        private IniConfig iniConfig;
        private WeekRecord gWeekRecord;
        private Boolean gNewDay;
        private int errorTime = 0;

        public void run()
        {
            textWriter($"{DateTime.Now},服务启动！", log);

            info = new ProcessInfo(0, new DateTime(1970, 1, 1), false);
            ini = new IniFile(@"C:\monitor\config.ini");

            List<string> names = new List<string>();
            names.Add(readIni(ini, "CONFIG", "name1"));
            names.Add(readIni(ini, "CONFIG", "name2"));
            names.Add(readIni(ini, "CONFIG", "name3"));
            names.Add(readIni(ini, "CONFIG", "name4"));
            names.Add(readIni(ini, "CONFIG", "name5"));

            int monitorPeriod = int.Parse(readIni(ini, "CONFIG", "monitorPeriod"));//观察周期60s
            int maxTimes = int.Parse(readIni(ini, "CONFIG", "maxTimes"));//最大玩的次数
            int validDuration = int.Parse(readIni(ini, "CONFIG", "validDuration"));//有效时长

            iniConfig = new IniConfig(names, path, monitorPeriod, maxTimes, validDuration);
            WeekRecord weekRecord = jsonFileToObject();
            //文件有数据读数据到weekRecord
            if (weekRecord != null)
            {
                objectToJsonFile(weekRecord, false, 0);
                int times = calcLastTimes(weekRecord);
                gWeekRecord = weekRecord;
                Interop.ShowMessageBox($"初始化时查询，今天还剩余{times}次", "提醒");
            }

            textWriter($"{DateTime.Now},iniConfig={iniConfig.ToString()}", log);
            runTimer();
        }
        private int calcLastTimes(WeekRecord weekRecord)
        {
            string url = "http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp";
            string getJson = HttpUitls.Get(url);
            JObject json = JObject.Parse(getJson);
            JObject data = (JObject)json.GetValue("data");
            string t = (string)data.GetValue("t");

            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            long n = Convert.ToInt64(ts.TotalSeconds);
            long x = long.Parse(t) / 1000 - n;

            if (Math.Abs(x) > 60 * 5)
            {
                errorTime++;
                if (errorTime > 5)
                {
                    Interop.ShowMessageBox($"不能改时间", "提醒");
                    errorTime = 0;
                    return 0;
                }
            }

            if (weekRecord != null)
            {
                if (weekRecord.WeekCount != getWeekOfYear(DateTime.Now))
                    return iniConfig.MaxTimes;

                foreach (DayRecord day in weekRecord.DayRecords)
                {
                    if (day.WeekDay == getTodayWeekDay())
                        return iniConfig.MaxTimes - day.GameRecords.Count;
                }
            }

            return iniConfig.MaxTimes;
        }

        private void runTimer()
        {
            System.Timers.Timer oneTimer = new System.Timers.Timer();
            oneTimer.Elapsed += new System.Timers.ElapsedEventHandler((obj, eventArg) =>
            {
                timerProcess();
            });
            oneTimer.Interval = iniConfig.MonitorPeriod * 1000;
            oneTimer.Enabled = true;
            oneTimer.AutoReset = true;
        }

        private void timerProcess()
        {
            Process[] pro = Process.GetProcesses();//获取已开启的所有进程
            for (int i = 0; i < pro.Length; i++)
            {
                //查询每一个进程，查询是否有需要监控的进程
                if (iniConfig.Names.Contains(pro[i].ProcessName.ToString()))
                {
                    int times = calcLastTimes(gWeekRecord);

                    //如果有需要监控的进程，首先查询是否已用尽次数
                    if (times <= 0)
                    {
                        killProcess();
                        ini.IniWriteValue("INFO", "record", "close");
                        return;
                    }

                    //监控程序打开的上升沿
                    if (info.LastStatus == false)
                    {


                        info.LastStatus = true;
                        textWriter("", log);
                        textWriter($"{DateTime.Now},name={pro[i].ProcessName.ToString()},监控程序打开的上升沿,id={pro[i].Id}", log);

                        //启动时间和pid暂时保存
                        info.StartTime = DateTime.Now;
                        info.Pid = pro[i].Id;

                        //判断服务是否重启而未记录关闭状态，
                        string record = ini.IniReadValue("INFO", "record");
                        //非正常关闭服务
                        if (record != "close")
                        {
                            string time = ini.IniReadValue("INFO", "time");
                            textWriter($"{DateTime.Now},上次非正常关闭服务，上次记录启动时间为{time}", log);
                            info.StartTime = DateTime.Parse(time);
                        }

                        ini.IniWriteValue("INFO", "record", "open");
                        ini.IniWriteValue("INFO", "time", DateTime.Now.ToString());
                    }

                    //更改状态为打开
                    info.LastStatus = true;
                    return;
                }
            }

            //没有找到到相关进程，监控程序下降沿
            if (info.LastStatus == true)
            {
                textWriter($"{DateTime.Now},进程关闭", log);
                GameRecord gameRecord = new GameRecord();
                gameRecord.StartTime = info.StartTime;
                gameRecord.EndTime = DateTime.Now;
                TimeSpan difTime = gameRecord.EndTime - gameRecord.StartTime;
                gameRecord.Duration = (int)difTime.TotalSeconds;

                //初始化info数据
                info.StartTime = new DateTime(1970, 1, 2);
                info.Pid = 0;

                ini.IniWriteValue("INFO", "record", "close");

                info.LastStatus = false;
                //小于有效使用时间10分钟
                if (gameRecord.Duration < iniConfig.ValidDuration)
                {
                    Interop.ShowMessageBox($"本次时间小于有效时间", "提醒");
                    textWriter($"{DateTime.Now},本次时间小于有效时间,ValidDuration={iniConfig.ValidDuration},Duration={gameRecord.Duration}", log);
                    return;
                }
                //存储游戏记录
                saveGameRecord(gameRecord);
                return;
            }
            info.LastStatus = false;
            return;
        }

        private void killProcess()
        {
            Process[] pro = Process.GetProcesses();//获取已开启的所有进程
            for (int i = 0; i < pro.Length; i++)
            {
                string name = pro[i].ProcessName.ToString();
                if (iniConfig.Names.Contains(name))
                {
                    textWriter($"{DateTime.Now},name={name} KILL!!!", log);
                    Interop.ShowMessageBox($"已用尽次数", "提醒");
                    pro[i].Kill();
                }
            }
        }
        private void textWriter(string str, string filePath)
        {
            using (FileStream stream = new FileStream(filePath, FileMode.Append))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.WriteLine(str);
                writer.Close();
            }
            Console.WriteLine(str);
        }
        private string readIni(IniFile ini, string section, string key)
        {
            string str = ini.IniReadValue(section, key);
            if (str == "")
                return null;
            return str;
        }
        private int getWeekOfYear(DateTime dt)
        {
            GregorianCalendar gc = new GregorianCalendar();
            return gc.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
        }
        private int getTodayWeekDay()
        {
            string dt = DateTime.Today.DayOfWeek.ToString();
            int week = 1;
            switch (dt)
            {
                case "Monday":
                    week = 1;
                    break;
                case "Tuesday":
                    week = 2;
                    break;
                case "Wednesday":
                    week = 3;
                    break;
                case "Thursday":
                    week = 4;
                    break;
                case "Friday":
                    week = 5;
                    break;
                case "Saturday":
                    week = 6;
                    break;
                case "Sunday":
                    week = 7;
                    break;
            }
            return week;
        }
        private DateTime getMondayDate(DateTime someDate)
        {
            int i = someDate.DayOfWeek - DayOfWeek.Monday;
            if (i == -1) i = 6;// i值 > = 0 ，因为枚举原因，Sunday排在最前，此时Sunday-Monday=-1，必须+7=6。
            TimeSpan ts = new TimeSpan(i, someDate.Hour, someDate.Minute, someDate.Second);
            return someDate.Subtract(ts);
        }
        private void saveGameRecord(GameRecord gameRecord)
        {
            //game数据为空跳出
            if (gameRecord == null)
            {
                textWriter($"{DateTime.Now},gameRecord数据为空！", log);
                return;
            }

            WeekRecord weekRecord = jsonFileToObject();

            //游戏次数默认等于最大值
            int lastTimes = iniConfig.MaxTimes;

            //首次创建周报
            if (weekRecord == null)
            {
                textWriter($"{DateTime.Now},第一次创建周报", log);
                Interop.ShowMessageBox($"第一次创建周报", "提醒");
                weekRecord = new WeekRecord();
                weekRecord.WeekCount = getWeekOfYear(DateTime.Now);
                weekRecord.WeekStart = getMondayDate(DateTime.Now);
                weekRecord.DayRecords = new List<DayRecord>();

                DayRecord dayRecord = new DayRecord();
                dayRecord.WeekDay = getTodayWeekDay();
                dayRecord.GameRecords = new List<GameRecord>();
                dayRecord.GameRecords.Add(gameRecord);
                weekRecord.DayRecords.Add(dayRecord);

                objectToJsonFile(weekRecord, false, 0);
                gWeekRecord = weekRecord;
                return;
            }

            //创建新的周报
            if (weekRecord.WeekCount != getWeekOfYear(DateTime.Now))
            {
                int cnt = weekRecord.WeekCount;
                textWriter($"{DateTime.Now},创建新的周报", log);
                Interop.ShowMessageBox($"创建新的周报", "提醒");

                weekRecord = new WeekRecord();
                weekRecord.WeekCount = getWeekOfYear(DateTime.Now);
                weekRecord.WeekStart = getMondayDate(DateTime.Now);
                weekRecord.DayRecords = new List<DayRecord>();

                DayRecord dayRecord = new DayRecord();
                dayRecord.WeekDay = getTodayWeekDay();
                dayRecord.GameRecords = new List<GameRecord>();
                dayRecord.GameRecords.Add(gameRecord);
                weekRecord.DayRecords.Add(dayRecord);


                objectToJsonFile(weekRecord, true, cnt);
                gWeekRecord = weekRecord;
                return;
            }

            //当天继续 遍历weekRecord.DayRecords，
            foreach (DayRecord day in weekRecord.DayRecords)
            {
                if (day.WeekDay == getTodayWeekDay())
                {
                    day.GameRecords.Add(gameRecord);
                    objectToJsonFile(weekRecord, false, 0);

                    textWriter($"{DateTime.Now},还是今天的继续记录", log);
                    Interop.ShowMessageBox($"还是今天的继续记录，今天还剩余{(iniConfig.MaxTimes - day.GameRecords.Count)}次", "提醒");
                    gWeekRecord = weekRecord;
                    return;
                }
            }



            //新的一天
            textWriter($"{DateTime.Now},新一天创建DayRecord", log);
            Interop.ShowMessageBox($"新的一天，今天还剩余{iniConfig.MaxTimes}次", "提醒");

            DayRecord newDayRecord = new DayRecord();
            newDayRecord.WeekDay = getTodayWeekDay();
            newDayRecord.GameRecords = new List<GameRecord>();
            newDayRecord.GameRecords.Add(gameRecord);

            weekRecord.DayRecords.Add(newDayRecord);
            objectToJsonFile(weekRecord, false, 0);
            gWeekRecord = weekRecord;
            return;
        }

        //从文件中读取到weekRecord
        private WeekRecord jsonFileToObject()
        {
            if (!File.Exists(iniConfig.Path))
            {
                return null;
            }
            string text = File.ReadAllText(iniConfig.Path);
            if (text == null || text == "") return null;
            JObject weekRecordObject = JObject.Parse(text);
            WeekRecord weekRecord = new WeekRecord();
            weekRecord.WeekCount = (int)weekRecordObject.GetValue("weekCount");
            weekRecord.WeekStart = (DateTime)weekRecordObject.GetValue("weekStart");

            List<DayRecord> dayRecords = new List<DayRecord>();

            JArray dayRecordsArray = (JArray)weekRecordObject.GetValue("dayRecords");
            if (dayRecordsArray != null && dayRecordsArray.Count > 0)
            {
                foreach (JObject dayRecordObject in dayRecordsArray)
                {
                    DayRecord dayRecord = new DayRecord();
                    dayRecord.WeekDay = (int)dayRecordObject.GetValue("weekDay");
                    List<GameRecord> gameRecords = new List<GameRecord>();

                    JArray gameRecordsArray = (JArray)dayRecordObject.GetValue("gameRecords");
                    if (gameRecordsArray != null && gameRecordsArray.Count > 0)
                    {
                        foreach (JObject gameRecordObject in gameRecordsArray)
                        {
                            GameRecord gameRecord = new GameRecord();
                            gameRecord.StartTime = (DateTime)gameRecordObject.GetValue("startTime");
                            gameRecord.EndTime = (DateTime)gameRecordObject.GetValue("endTime");
                            gameRecord.Duration = (int)gameRecordObject.GetValue("duration");
                            gameRecords.Add(gameRecord);
                        }
                    }
                    dayRecord.GameRecords = gameRecords;
                    dayRecords.Add(dayRecord);
                }
            }
            weekRecord.DayRecords = dayRecords;

            return weekRecord;
        }

        //把weekRecord写到文件中
        private void objectToJsonFile(WeekRecord weekRecord, bool oldSave, int cnt)
        {
            JObject top = new JObject();
            top.Add("weekCount", weekRecord.WeekCount);
            top.Add("weekStart", weekRecord.WeekStart);

            JArray dayRecords = new JArray();
            foreach (DayRecord dayRecord in weekRecord.DayRecords)
            {

                JObject dayJson = new JObject();
                dayJson.Add("weekDay", dayRecord.WeekDay);

                JArray gameRecords = new JArray();
                foreach (GameRecord gameRecord in dayRecord.GameRecords)
                {
                    JObject gameJson = new JObject();
                    gameJson.Add("startTime", gameRecord.StartTime);
                    gameJson.Add("endTime", gameRecord.EndTime);
                    gameJson.Add("duration", gameRecord.Duration);
                    gameRecords.Add(gameJson);
                }
                dayJson.Add("gameRecords", gameRecords);
                dayRecords.Add(dayJson);
            }
            top.Add("dayRecords", dayRecords);

            //新的一周 先复制原文件，再进行写入
            if (oldSave)
            {
                string destinationFile = $"C:\\json-{cnt}.txt";
                System.IO.File.Copy(path, destinationFile, true);
            }
            File.WriteAllText(iniConfig.Path, top.ToString());
            File.WriteAllText(copyJson, top.ToString());
            return;
        }
    }
}
